package com.tianji.learning.handler;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.service.IPointsBoardSeasonService;
import com.tianji.learning.service.IPointsBoardService;
import com.tianji.learning.utils.TableInfoContext;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.List;
import static com.tianji.learning.constants.LearningConstants.POINTS_BOARD_TABLE_PREFIX;

//持久化榜单数据
@Component
@RequiredArgsConstructor
public class PointsBoardPersistentHandler {

    private final IPointsBoardSeasonService seasonService;

    private final IPointsBoardService pointsBoardService;

    private final StringRedisTemplate redisTemplate;

    @XxlJob("createTableJob") // 每月1号，凌晨3点执行
    public void createPointsBoardTableOfLastSeason(){
        // 1.获取上月时间
        //2023-8-1
        LocalDateTime time = LocalDateTime.now().minusMonths(1);

        // 2.根据时间范围，查询赛季信息表，拿到赛季id
        Integer season = seasonService.querySeasonByTime(time);
        if (season == null) {
            // 赛季不存在
            return;
        }
        // 3.创建表：新赛季，历史榜单表
        pointsBoardService.createPointsBoardTableBySeason(season);
    }


    //持久化上个月榜单数据，存入mysql
    @XxlJob("savePointsBoard2DB")
    public void savePointsBoard2DB(){
        // 1.获取上月时间
        LocalDateTime time = LocalDateTime.now().minusMonths(1);

        //2.计算动态表名
        Integer season = seasonService.querySeasonByTime(time);
        if (season == null) {
            // 赛季不存在
            return;
        }
        //points_board_ + 5
        String tableName = POINTS_BOARD_TABLE_PREFIX + season;

        // 2.1.将表名称存入ThreadLocal
        TableInfoContext.setInfo(tableName);

        //3.查询Redis中上个月榜单数据
        //3.1.拼接Redis的Key：boards:202382
        String key = RedisConstants.POINTS_BOARD_KEY_PREFIX + time.format(DateUtils.POINTS_BOARD_SUFFIX_FORMATTER);

        //3.2.分页查询Redis中的榜单列表数据
        //起始页码：从第几页开始；  取值为：执行器编号（数据分片的序号）只不过是从0开始
        int index = XxlJobHelper.getShardIndex();   //0     1  3   5  7

        //下一页的跨度：跳过别人读取过的页码； 取值为：分片的总数量（执行器集群的实例总数）
        int total = XxlJobHelper.getShardTotal();   //2     2  4   6  8

        //分页的起始页码，就是分片序号+1
        int pageNo = index + 1;  //1
        int pageSize = 1000;
        while (true) {
            List<PointsBoard> boardList = pointsBoardService.queryCurrentBoardList(key, pageNo, pageSize);
            if (CollUtils.isEmpty(boardList)) {
                //Redis中没有数据需要持久化，直接结束
                 break;
            }
            //4.持久化榜单数据，到数据库
            //4.1.把排名信息写入id，这样数据库的id顺序也就表示积分排名顺序。
            boardList.forEach(b ->{
                b.setId(b.getRank().longValue());
                //字段值为null，则这个字段不插入数据库
                b.setRank(null);
            });

            //4.2.持久化
            pointsBoardService.saveBatch(boardList);

            //5.翻页，跳过N个页，N就是分片数量。跳过的页交给其他实例去执行。
            pageNo+=total;

            //6.清理ThreadLocal
            TableInfoContext.remove();
        }
    }

    //清除Redis上月积分榜单数据
    @XxlJob("clearPointsBoardFromRedis")
    public void clearPointsBoardFromRedis(){
        // 1.获取上月时间
        LocalDateTime time = LocalDateTime.now().minusMonths(1);
        // 2.计算Redis的key
        String key = RedisConstants.POINTS_BOARD_KEY_PREFIX + time.format(DateUtils.POINTS_BOARD_SUFFIX_FORMATTER);
        // 3.删除上月榜单数据
        redisTemplate.unlink(key);
    }
}